// Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{collections::BTreeMap, path::{Path, PathBuf}};

use anyhow::Context;
use serde::{de::Visitor, ser::SerializeMap, Deserialize, Serialize};
use sha2::{digest::Update, Sha256};

use crate::{deserialize_map_case, graph::build_dep_graph::DepNode};

const DEFAULT_SRC_DIR: &str = "${pkg}";
const DEFAULT_BUILD_DIR: &str = "build";
const DEFAULT_CONFIGURE_PATH: &str = "${pkg}/configure";

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BuildConfig {
    pub method: BuildMethod,
    pub options: BuildOptions,
    pub envs: Option<BuildEnvsConfig>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BuildConfigOverrides {
    pub method: Option<BuildMethodOverrides>,
    pub options: Option<BuildOptionsOverrides>,
    pub envs: Option<BuildEnvsConfigOverrides>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BuildMethod {
    Custom(CustomBuildConfig),
    Configure(ConfigureBuildConfig),
    Meson(MesonBuildConfig),
    CMake(CMakeBuildConfig),
    Pip(PipBuildConfig),
    Cargo(CargoBuildConfig),
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum BuildMethodOverrides {
    Custom(CustomBuildConfigOverrides),
    Configure(ConfigureBuildConfigOverrides),
    Meson(MesonBuildConfigOverrides),
    CMake(CMakeBuildConfigOverrides),
    Pip(PipBuildConfigOverrides),
    Cargo(CargoBuildConfigOverrides),
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct CustomBuildConfig {
    pub build_script: PathBuf,
    pub install_script: PathBuf,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct CustomBuildConfigOverrides {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub build_script: Option<PathBuf>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub install_script: Option<PathBuf>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct ConfigureBuildConfig {
    #[serde(default = "TemplatePath::default_configure_path", skip_serializing_if = "TemplatePath::is_default_configure_path")]
    pub configure: TemplatePath,
    #[serde(default = "TemplatePath::default_build_dir", skip_serializing_if = "TemplatePath::is_default_build_dir")]
    pub build_dir: TemplatePath,
    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
    pub autoreconf: bool,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extra_args: Option<Vec<String>>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct ConfigureBuildConfigOverrides {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub configure: Option<TemplatePath>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub build_dir: Option<TemplatePath>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub autoreconf: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extra_args: Option<Vec<String>>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct MesonBuildConfig {
    #[serde(default = "TemplatePath::default_src_dir", skip_serializing_if = "TemplatePath::is_default_src_dir")]
    pub src_dir: TemplatePath,
    #[serde(default = "TemplatePath::default_build_dir", skip_serializing_if = "TemplatePath::is_default_build_dir")]
    pub build_dir: TemplatePath,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extra_args: Option<Vec<String>>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct MesonBuildConfigOverrides {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub src_dir: Option<TemplatePath>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub build_dir: Option<TemplatePath>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extra_args: Option<Vec<String>>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct CMakeBuildConfig {
    #[serde(default = "TemplatePath::default_src_dir", skip_serializing_if = "TemplatePath::is_default_src_dir")]
    pub src_dir: TemplatePath,
    #[serde(default = "TemplatePath::default_build_dir", skip_serializing_if = "TemplatePath::is_default_build_dir")]
    pub build_dir: TemplatePath,
    #[serde(default, skip_serializing_if = "CMakeBuilder::is_default")]
    pub builder: CMakeBuilder,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extra_args: Option<Vec<String>>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct CMakeBuildConfigOverrides {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub src_dir: Option<TemplatePath>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub build_dir: Option<TemplatePath>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub builder: Option<CMakeBuilder>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extra_args: Option<Vec<String>>,
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum CMakeBuilder {
    #[default]
    Make,
    Ninja,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct PipBuildConfig {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub rename: Option<String>,
    #[serde(default = "TemplatePath::default_src_dir", skip_serializing_if = "TemplatePath::is_default_src_dir")]
    pub src_dir: TemplatePath,
    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
    pub local: bool,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extra_args: Option<Vec<String>>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct PipBuildConfigOverrides {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub rename: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub src_dir: Option<TemplatePath>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub local: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extra_args: Option<Vec<String>>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct CargoBuildConfig {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub rename: Option<String>,
    #[serde(default = "TemplatePath::default_src_dir", skip_serializing_if = "TemplatePath::is_default_src_dir")]
    pub src_dir: TemplatePath,
    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
    pub local: bool,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extra_args: Option<Vec<String>>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct CargoBuildConfigOverrides {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub rename: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub src_dir: Option<TemplatePath>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub local: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extra_args: Option<Vec<String>>,
}

#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct BuildOptions {
    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
    pub need_run_compiled: bool,
    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
    pub dont_need_cross_compile: bool,
    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
    pub no_python_cross_soabi: bool,
}

#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct BuildOptionsOverrides {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub need_run_compiled: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub dont_need_cross_compile: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub no_python_cross_soabi: Option<bool>,
}

#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct BuildEnvsConfig {
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub envs: BTreeMap<String, String>,
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub cross_envs: BTreeMap<String, String>,
}

#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct BuildEnvsConfigOverrides {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub envs: Option<BTreeMap<String, String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub cross_envs: Option<BTreeMap<String, String>>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TemplatePath(String);

impl Serialize for BuildConfig {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer
    {
        let serialize_options = !self.options.is_default();
        let len = serialize_options as usize + 1;
        let mut map = serializer.serialize_map(Some(len))?;
        if serialize_options {
            map.serialize_entry("options", &self.options)?;
        }
        match &self.method {
            BuildMethod::Custom(config) => map.serialize_entry("custom", config)?,
            BuildMethod::Configure(config) => map.serialize_entry("configure", config)?,
            BuildMethod::Meson(config) => map.serialize_entry("meson", config)?,
            BuildMethod::CMake(config) => map.serialize_entry("cmake", config)?,
            BuildMethod::Pip(config) => map.serialize_entry("pip", config)?,
            BuildMethod::Cargo(config) => map.serialize_entry("cargo", config)?,
        }
        map.end()
    }
}

#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "kebab-case")]
enum BuildConfigField {
    Options,
    Envs,
    CrossEnvs,
    Custom,
    Configure,
    Meson,
    #[serde[rename = "cmake"]]
    CMake,
    Pip,
    Cargo,
}

impl<'de> Deserialize<'de> for BuildConfig {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>
    {
        struct BuildConfigVisitor;

        impl<'de> Visitor<'de> for BuildConfigVisitor {
            type Value = BuildConfig;
        
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("build config")
            }

            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
                where
                    A: serde::de::MapAccess<'de>,
            {
                const METHOD_DUPLICATED_ERR_MSG: &str = "only one of custom, configure, meson, pip, cargo";
                const METHOD_MISSING_ERR_MSG: &str = "one of custom, configure, meson, pip, cargo";

                let mut options = None;
                let mut method = None;
                let mut envs = None;
                let mut cross_envs = None;

                while let Some(k) = map.next_key()? {
                    match k {
                        BuildConfigField::Options => deserialize_map_case!(map, options, "options"),
                        BuildConfigField::Envs => deserialize_map_case!(map, envs, "envs"),
                        BuildConfigField::CrossEnvs => deserialize_map_case!(map, cross_envs, "cross-envs"),
                        BuildConfigField::Custom => {
                            if method.is_some() {
                                return Err(serde::de::Error::custom(METHOD_DUPLICATED_ERR_MSG));
                            }
                            method = Some(BuildMethod::Custom(map.next_value()?));
                        },
                        BuildConfigField::Configure => {
                            if method.is_some() {
                                return Err(serde::de::Error::custom(METHOD_DUPLICATED_ERR_MSG));
                            }
                            method = Some(BuildMethod::Configure(map.next_value()?));
                        },
                        BuildConfigField::Meson => {
                            if method.is_some() {
                                return Err(serde::de::Error::custom(METHOD_DUPLICATED_ERR_MSG));
                            }
                            method = Some(BuildMethod::Meson(map.next_value()?));
                        },
                        BuildConfigField::CMake => {
                            if method.is_some() {
                                return Err(serde::de::Error::custom(METHOD_DUPLICATED_ERR_MSG));
                            }
                            method = Some(BuildMethod::CMake(map.next_value()?));
                        },
                        BuildConfigField::Pip => {
                            if method.is_some() {
                                return Err(serde::de::Error::custom(METHOD_DUPLICATED_ERR_MSG));
                            }
                            method = Some(BuildMethod::Pip(map.next_value()?));
                        },
                        BuildConfigField::Cargo => {
                            if method.is_some() {
                                return Err(serde::de::Error::custom(METHOD_DUPLICATED_ERR_MSG));
                            }
                            method = Some(BuildMethod::Cargo(map.next_value()?));
                        },
                    }
                }

                let options = options.unwrap_or_default();
                let method = method.ok_or(serde::de::Error::missing_field(METHOD_MISSING_ERR_MSG))?;
                let envs_config = if envs.is_none() && cross_envs.is_none() {
                    None
                } else {
                    Some(BuildEnvsConfig {
                        envs: envs.unwrap_or_default(),
                        cross_envs: cross_envs.unwrap_or_default(),
                    })
                };
                Ok(BuildConfig {
                    method,
                    options,
                    envs: envs_config,
                })
            }
        }

        deserializer.deserialize_map(BuildConfigVisitor)
    }
}

impl Serialize for BuildConfigOverrides {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer
    {
        let len = self.options.is_some() as usize + self.method.is_some() as usize;
        let mut map = serializer.serialize_map(Some(len))?;
        if let Some(options) = self.options.as_ref() {
            map.serialize_entry("options", options)?;
        }
        if let Some(method) = self.method.as_ref() {
            match method {
                BuildMethodOverrides::Custom(config) => map.serialize_entry("custom", config)?,
                BuildMethodOverrides::Configure(config) => map.serialize_entry("configure", config)?,
                BuildMethodOverrides::Meson(config) => map.serialize_entry("meson", config)?,
                BuildMethodOverrides::CMake(config) => map.serialize_entry("cmake", config)?,
                BuildMethodOverrides::Pip(config) => map.serialize_entry("pip", config)?,
                BuildMethodOverrides::Cargo(config) => map.serialize_entry("cargo", config)?,
            }
        }
        map.end()
    }
}

impl<'de> Deserialize<'de> for BuildConfigOverrides {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>
    {
        struct BuildConfigOverridesVisitor;

        impl<'de> Visitor<'de> for BuildConfigOverridesVisitor {
            type Value = BuildConfigOverrides;
        
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("build config")
            }

            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
                where
                    A: serde::de::MapAccess<'de>,
            {
                const METHOD_DUPLICATED_ERR_MSG: &str = "only one of custom, configure, meson, pip, cargo";

                let mut options = None;
                let mut method = None;
                let mut envs = None;
                let mut cross_envs = None;

                while let Some(k) = map.next_key()? {
                    match k {
                        BuildConfigField::Options => deserialize_map_case!(map, options, "options"),
                        BuildConfigField::Envs => deserialize_map_case!(map, envs, "envs"),
                        BuildConfigField::CrossEnvs => deserialize_map_case!(map, cross_envs, "cross-envs"),
                        BuildConfigField::Custom => {
                            if method.is_some() {
                                return Err(serde::de::Error::custom(METHOD_DUPLICATED_ERR_MSG));
                            }
                            method = Some(BuildMethodOverrides::Custom(map.next_value()?));
                        },
                        BuildConfigField::Configure => {
                            if method.is_some() {
                                return Err(serde::de::Error::custom(METHOD_DUPLICATED_ERR_MSG));
                            }
                            method = Some(BuildMethodOverrides::Configure(map.next_value()?));
                        },
                        BuildConfigField::Meson => {
                            if method.is_some() {
                                return Err(serde::de::Error::custom(METHOD_DUPLICATED_ERR_MSG));
                            }
                            method = Some(BuildMethodOverrides::Meson(map.next_value()?));
                        },
                        BuildConfigField::CMake => {
                            if method.is_some() {
                                return Err(serde::de::Error::custom(METHOD_DUPLICATED_ERR_MSG));
                            }
                            method = Some(BuildMethodOverrides::CMake(map.next_value()?));
                        },
                        BuildConfigField::Pip => {
                            if method.is_some() {
                                return Err(serde::de::Error::custom(METHOD_DUPLICATED_ERR_MSG));
                            }
                            method = Some(BuildMethodOverrides::Pip(map.next_value()?));
                        },
                        BuildConfigField::Cargo => {
                            if method.is_some() {
                                return Err(serde::de::Error::custom(METHOD_DUPLICATED_ERR_MSG));
                            }
                            method = Some(BuildMethodOverrides::Cargo(map.next_value()?));
                        },
                    }
                }

                let envs_config = if envs.is_none() && cross_envs.is_none() {
                    None
                } else {
                    Some(BuildEnvsConfigOverrides {
                        envs: envs.unwrap_or_default(),
                        cross_envs: cross_envs.unwrap_or_default(),
                    })
                };
                Ok(BuildConfigOverrides {
                    method,
                    options,
                    envs: envs_config,
                })
            }
        }
    
        deserializer.deserialize_map(BuildConfigOverridesVisitor)
    }
}

impl Default for ConfigureBuildConfig {
    fn default() -> Self {
        Self {
            configure: TemplatePath::default_configure_path(),
            build_dir: TemplatePath::default_build_dir(),
            autoreconf: Default::default(),
            extra_args: None,
        }
    }
}

impl Default for MesonBuildConfig {
    fn default() -> Self {
        Self {
            src_dir: TemplatePath::default_src_dir(),
            build_dir: TemplatePath::default_build_dir(),
            extra_args: None,
        }
    }
}

impl BuildConfig {
    pub fn merge_overrides(&mut self, overrides: &BuildConfigOverrides) {
        if let Some(options_overrides) = overrides.options.as_ref() {
            self.options.merge_overrides(options_overrides);
        }
        if let Some(envs_overrides) = overrides.envs.as_ref() {
            if let Some(envs) = self.envs.as_mut() {
                envs.merge_overrides(envs_overrides);
            } else {
                let mut envs = BuildEnvsConfig::default();
                envs.merge_overrides(envs_overrides);
                self.envs = Some(envs);
            }
        }
        if let Some(method_overrides) = overrides.method.as_ref() {
            self.method.merge_overrides(method_overrides);
        }
    }

    pub async fn spec_hash<P: AsRef<Path>>(&self, hasher: &mut Sha256, base_dir: P, cross_compile: bool) -> anyhow::Result<()> {
        self.method.spec_hash(hasher, base_dir).await?;
        self.options.spec_hash(hasher);
        if let Some(envs) = self.envs.as_ref() {
            envs.spec_hash(hasher, cross_compile);
        }
        Ok(())
    }
}

impl BuildMethod {
    fn merge_overrides(&mut self, overrides: &BuildMethodOverrides) {
        match (self, overrides) {
            (BuildMethod::Custom(c), BuildMethodOverrides::Custom(overrides)) => c.merge_overrides(overrides),
            (BuildMethod::Configure(c), BuildMethodOverrides::Configure(overrides)) => c.merge_overrides(overrides),
            (BuildMethod::Meson(c), BuildMethodOverrides::Meson(overrides)) => c.merge_overrides(overrides),
            (BuildMethod::CMake(c), BuildMethodOverrides::CMake(overrides)) => c.merge_overrides(overrides),
            (BuildMethod::Pip(c), BuildMethodOverrides::Pip(overrides)) => c.merge_overrides(overrides),
            (BuildMethod::Cargo(c), BuildMethodOverrides::Cargo(overrides)) => c.merge_overrides(overrides),
            _ => {} // TODO: error
        }
    }

    async fn spec_hash<P: AsRef<Path>>(&self, hasher: &mut Sha256, base_dir: P) -> anyhow::Result<()> {
        match self {
            BuildMethod::Custom(c) => c.spec_hash(hasher, base_dir).await?,
            BuildMethod::Configure(c) => c.spec_hash(hasher),
            BuildMethod::Meson(c) => c.spec_hash(hasher),
            BuildMethod::CMake(c) => c.spec_hash(hasher),
            BuildMethod::Pip(c) => c.spec_hash(hasher),
            BuildMethod::Cargo(c) => c.spec_hash(hasher),
        }
        Ok(())
    }
}

impl CustomBuildConfig {
    fn merge_overrides(&mut self, overrides: &CustomBuildConfigOverrides) {
        if let Some(build_script) = overrides.build_script.as_ref() {
            self.build_script = build_script.clone();
        }
        if let Some(install_script) = overrides.install_script.as_ref() {
            self.install_script = install_script.clone();
        }
    }

    async fn spec_hash<P: AsRef<Path>>(&self, hasher: &mut Sha256, base_dir: P) -> anyhow::Result<()> {
        let base_dir = base_dir.as_ref();
        let build_script_path = base_dir.join(&self.build_script);
        let install_script_path = base_dir.join(&self.install_script);
        let build_script_content = tokio::fs::read_to_string(&build_script_path).await
            .context(format!("failed to read build script {}", build_script_path.display()))?;
        let install_script_content = tokio::fs::read_to_string(&install_script_path).await
            .context(format!("failed to read install script {}", install_script_path.display()))?;
        hasher.update(build_script_content.as_bytes());
        hasher.update(install_script_content.as_bytes());
        Ok(())
    }
}

impl ConfigureBuildConfig {
    fn merge_overrides(&mut self, overrides: &ConfigureBuildConfigOverrides) {
        if let Some(configure) = overrides.configure.as_ref() {
            self.configure = configure.clone();
        }
        if let Some(build_dir) = overrides.build_dir.as_ref() {
            self.build_dir = build_dir.clone();
        }
        if let Some(autoreconf) = overrides.autoreconf.as_ref() {
            self.autoreconf = *autoreconf;
        }
        if let Some(extra_args) = overrides.extra_args.as_ref() {
            self.extra_args = Some(extra_args.clone());
        }
    }

    fn spec_hash(&self, hasher: &mut Sha256) {
        hasher.update(self.configure.as_bytes());
        hasher.update(self.build_dir.as_bytes());
        hasher.update(&[self.autoreconf as u8]);
        if let Some(extra_args) = self.extra_args.as_ref() {
            for arg in extra_args {
                hasher.update(arg.as_bytes());
            }
        }
    }
}

impl MesonBuildConfig {
    fn merge_overrides(&mut self, overrides: &MesonBuildConfigOverrides) {
        if let Some(src_dir) = overrides.src_dir.as_ref() {
            self.src_dir = src_dir.clone();
        }
        if let Some(build_dir) = overrides.build_dir.as_ref() {
            self.build_dir = build_dir.clone();
        }
        if let Some(extra_args) = overrides.extra_args.as_ref() {
            self.extra_args = Some(extra_args.clone());
        }
    }

    fn spec_hash(&self, hasher: &mut Sha256) {
        hasher.update(self.src_dir.as_bytes());
        hasher.update(self.build_dir.as_bytes());
        if let Some(extra_args) = self.extra_args.as_ref() {
            for arg in extra_args {
                hasher.update(arg.as_bytes());
            }
        }
    }
}

impl CMakeBuildConfig {
    fn merge_overrides(&mut self, overrides: &CMakeBuildConfigOverrides) {
        if let Some(src_dir) = overrides.src_dir.as_ref() {
            self.src_dir = src_dir.clone();
        }
        if let Some(build_dir) = overrides.build_dir.as_ref() {
            self.build_dir = build_dir.clone();
        }
        if let Some(builder) = overrides.builder.as_ref() {
            self.builder = *builder;
        }
        if let Some(extra_args) = overrides.extra_args.as_ref() {
            self.extra_args = Some(extra_args.clone());
        }
    }

    fn spec_hash(&self, hasher: &mut Sha256) {
        hasher.update(self.src_dir.as_bytes());
        hasher.update(self.build_dir.as_bytes());
        hasher.update(&[self.builder as u8]);
        if let Some(extra_args) = self.extra_args.as_ref() {
            for arg in extra_args {
                hasher.update(arg.as_bytes());
            }
        }
    }
}

impl PipBuildConfig {
    fn merge_overrides(&mut self, overrides: &PipBuildConfigOverrides) {
        if let Some(src_dir) = overrides.src_dir.as_ref() {
            self.src_dir = src_dir.clone();
        }
        if let Some(rename) = overrides.rename.as_ref() {
            self.rename = Some(rename.clone());
        }
        if let Some(local) = overrides.local.as_ref() {
            self.local = *local;
        }
        if let Some(extra_args) = overrides.extra_args.as_ref() {
            self.extra_args = Some(extra_args.clone());
        }
    }

    fn spec_hash(&self, hasher: &mut Sha256) {
        hasher.update(&[self.local as u8]);
        if self.local {
            hasher.update(self.src_dir.as_bytes());
        } else {
            if let Some(ref rename) = self.rename {
                hasher.update(rename.as_bytes());
            }
        }
        if let Some(extra_args) = self.extra_args.as_ref() {
            for arg in extra_args {
                hasher.update(arg.as_bytes());
            }
        }
    }
}

impl CargoBuildConfig {
    fn merge_overrides(&mut self, overrides: &CargoBuildConfigOverrides) {
        if let Some(src_dir) = overrides.src_dir.as_ref() {
            self.src_dir = src_dir.clone();
        }
        if let Some(rename) = overrides.rename.as_ref() {
            self.rename = Some(rename.clone());
        }
        if let Some(local) = overrides.local.as_ref() {
            self.local = *local;
        }
        if let Some(extra_args) = overrides.extra_args.as_ref() {
            self.extra_args = Some(extra_args.clone());
        }
    }

    fn spec_hash(&self, hasher: &mut Sha256) {
        hasher.update(&[self.local as u8]);
        if self.local {
            hasher.update(self.src_dir.as_bytes());
        } else {
            if let Some(ref rename) = self.rename {
                hasher.update(rename.as_bytes());
            }
        }
        if let Some(extra_args) = self.extra_args.as_ref() {
            for arg in extra_args {
                hasher.update(arg.as_bytes());
            }
        }
    }
}

impl BuildOptions {
    fn is_default(&self) -> bool {
        *self == Self::default()
    }

    fn merge_overrides(&mut self, overrides: &BuildOptionsOverrides) {
        if let Some(need_run_compiled) = overrides.need_run_compiled {
            self.need_run_compiled = need_run_compiled;
        }
        if let Some(dont_need_cross_compile) = overrides.dont_need_cross_compile {
            self.dont_need_cross_compile = dont_need_cross_compile;
        }
        if let Some(no_python_cross_soabi) = overrides.no_python_cross_soabi {
            self.no_python_cross_soabi = no_python_cross_soabi;
        }
    }

    fn spec_hash(&self, hasher: &mut Sha256) {
        hasher.update(&[self.no_python_cross_soabi as u8]);
    }
}

impl BuildEnvsConfig {
    fn merge_overrides(&mut self, overrides: &BuildEnvsConfigOverrides) {
        if let Some(envs) = overrides.envs.as_ref() {
            self.envs = envs.clone();
        }
        if let Some(cross_envs) = overrides.cross_envs.as_ref() {
            self.cross_envs = cross_envs.clone();
        }
    }

    fn spec_hash(&self, hasher: &mut Sha256, cross_compile: bool) {
        for (k, v) in self.envs.iter() {
            hasher.update(k.as_bytes());
            hasher.update(v.as_bytes());
        }
        if cross_compile {
            for (k, v) in self.cross_envs.iter() {
                hasher.update(k.as_bytes());
                hasher.update(v.as_bytes());
            }
        }
    }
}

impl CMakeBuilder {
    fn is_default(&self) -> bool {
        *self == Self::default()
    }
}

impl TemplatePath {
    fn default_src_dir() -> Self {
        Self(String::from(DEFAULT_SRC_DIR))
    }

    fn default_build_dir() -> Self {
        Self(String::from(DEFAULT_BUILD_DIR))
    }

    fn default_configure_path() -> Self {
        Self(String::from(DEFAULT_CONFIGURE_PATH))
    }

    fn is_default_src_dir(&self) -> bool {
        self.0 == DEFAULT_SRC_DIR
    }

    fn is_default_build_dir(&self) -> bool {
        self.0 == DEFAULT_BUILD_DIR
    }

    fn is_default_configure_path(&self) -> bool {
        self.0 == DEFAULT_CONFIGURE_PATH
    }

    pub fn parse(&self, node: &DepNode) -> String {
        self.0
            .replace("${pkg}", &node.spec.pkg.to_string())
            .replace("${pkgname}", &node.spec.pkg.name)
            .replace("${pkgver}", node.spec.pkg.version.as_str())
    }

    pub fn as_bytes(&self) -> &[u8] {
        self.0.as_bytes()
    }
}